package bintree.heap;

/**
 * 基于整形最大堆实现
 * 时根节点从0开始编号,若此时节点编号为k
 * 左孩子为2k+1
 * 右孩子为2k+2
 * 父节点为（k-1）/2
 */

import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;

public class MaxHeap {
    // 使用JDK的动态数组(ArrayList)来存储一个最大堆
    List<Integer> data;

    // 构造方法的this调用
    public MaxHeap(){
        this(10);
    }
    
    // 初始化的堆大小
    public MaxHeap(int size){
        data = new ArrayList<>(size);
    }

    /**
     * 将任意数组堆化
     * @param arr
     */
    public MaxHeap(int[] arr){
        data = new ArrayList<>(arr.length);
        // 1.先将arr的所有元素复制到data数组中
        for(int i : arr){
            data.add(i);
        }
        // 2.从最后一个非叶子结点开始进行siftDown
        for (int i = parent(data.size()-1); i >=0 ; i--) {
            siftDown(i);
        }
    }

    /**
     * 向最大堆中增加值为Value的元素
     * @param value
     */
    public void add(int value){
        //1.先直接加到堆的末尾
        data.add(value);
        //2.元素上浮操作
        siftUp(data.size()-1);
    }

    /**
     * 只找到堆顶元素值
     * @return
     */
    public int peekMax (){
        if(isEmpty()){
            throw new NoSuchElementException("heap is empty!connot peek");
        }
        return data.get(0);
    }

    /**
     * 取出当前最大堆的最大值
     */
    public int extractMax(){
        // 取值一定注意判空
        if(isEmpty()){
            throw new NoSuchElementException("heap is empty!connot extract");
        }
        int max = data.get(0);
        // 1.将数组末尾元素顶到堆顶
        int lastValue =data.get(data.size()-1);
        data.set(0,lastValue);
        // 2.将数组末尾的元素删除
        data.remove(data.size()-1);
        // 3.进行元素的下沉操作
        siftDown(0);
        return max;
    }

    /**
     * 下沉操作
     */
    public void siftDown(int k){
        //还存在子树
        while (leftChild(k) < data.size()){
            int j = leftChild(k);
            //判断是否存在右子树且大于左子树的值
            if(j+1 < data.size() && data.get(j+1) > data.get(j)){
                j=j+1;
            }
            //此时j为左右子树最大值
            //和当前节点比较大小
            if(data.get(j) <= data.get(k)){
                break;
            }else {
                swap(k,j);
                k=j;
            }
        }
    }

    /**
     * 上浮操作
     */
    // 上浮操作的终止条件: 已经走到根节点 || 当前节点值 <= 父节点值
    // 循环的迭代条件 : 还存在父节点并且当前节点值 > 父节点值
    private void siftUp(int k) {
        while (k>0 && data.get(k)>data.get(parent(k))){
            swap(k,parent(k));
            k=parent(k);
        }
    }

    //交换三连
    private void swap(int i,int j) {
        int temp = data.get(j);
        data.set(j,data.get(i));
        data.set(i,temp);
    }

    //判读堆为空
    public boolean isEmpty(){
        return data.size() == 0;
    }
    //根据索引找父节点
    public int parent(int k){
        return (k-1)>>1;
    }
    //根据索引找左孩子
    public int leftChild(int k){
        return k<<2+1;
    }
    //根据索引找右孩子
    public int rightChild(int k){
        return k<<2+2;
    }

    @Override
    public String toString() {
        return data.toString();
    }
}
